namespace Roids.SL
{
    using System;
    using Framework;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;

    public class SpaceShip : SceneItem
    {
        private const float Angle180 = (float) Math.PI;
        private const float MaxAccel = 100f;
        private const float MaxThrust = 100f;
        private const float MaxVelocity = 150f;

        private readonly SpaceShipBlasts _blastsManager;
        private readonly Game _game;
        private readonly SpriteSheet _spriteMap;
        public float FrictionFactor = .5f;

        private SpriteFont _font;
        private DateTime _lastLaserFire = DateTime.Now;
        private DateTime _lastThrustEmit = DateTime.Now;
        private float _spin;
        private Texture2D _spriteSheet;
        private float _thrust;

        public SpaceShip(Game game, SpaceShipBlasts blastsManager) : base(game)
        {
            _game = game;
            _blastsManager = blastsManager;

            _spriteMap = new SpriteSheet(64, 64, 640, 256);
            _spriteMap.InitializeScenes(new[] {19, 19});

            Rotation = Angle180;
        }

        public override void LoadContent()
        {
            _spriteSheet = _game.Content.Load<Texture2D>(@"Sprites\StarShip");
            _font = _game.Content.Load<SpriteFont>(@"mainFont");

            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            var elapsedTime = gameTime.ElapsedGameTime;

            this.WrapPositionIfCrossing(_game.Window.ClientBounds);

            var pad = GamePad.GetState(PlayerIndex.One);

            if (pad.Buttons.A == ButtonState.Pressed)
            {
                FireLaser();
            }

            HandleRotation(pad, elapsedTime);

            HandleThrust(pad);

            Acceleration = (_thrust != 0)
                               ? ApplyThrust(_thrust)
                               : ApplyFriction();

            base.Update(gameTime);

            ClampVelocity();
        }

        private void HandleThrust(GamePadState pad)
        {
#if SILVERLIGHT
            HandleThrustOnKeyboard(pad);
#else
            HandleThrustOnGamePad(pad);
#endif
            if (Math.Abs(_thrust) < 0.001)
            {
                _thrust = 0;
            }
            else
            {
                _thrust = Math.Min(_thrust, 1.0f);
                _thrust = Math.Max(_thrust, -1.0f);
            }
        }

        private void HandleThrustOnKeyboard(GamePadState pad)
        {
            if (pad.DPad.Up == ButtonState.Pressed)
            {
                _thrust += 0.01f;
            }
            else if (pad.DPad.Down == ButtonState.Pressed)
            {
                //_thrust -= 0.01f;
            }
            else
            {
                _thrust *= 0.9f;
            }
        }

        private void HandleThrustOnGamePad(GamePadState pad)
        {
            var thumb = pad.ThumbSticks.Left.Y;

            if (thumb != 0)
            {
                _thrust += thumb*0.02f;
            }
            else
            {
                _thrust *= 0.9f;
            }
        }

        private void HandleRotation(GamePadState pad, TimeSpan elapsedTime)
        {
#if SILVERLIGHT
            HandleRotationOnKeyboard(pad);
#else
            HandleRotationOnGamePad(pad);
#endif
            Rotation -= (float) (_spin*elapsedTime.TotalSeconds*3.0);
        }

        private void HandleRotationOnKeyboard(GamePadState pad)
        {
            if (pad.DPad.Right == ButtonState.Pressed)
            {
                _spin -= 0.05f;
            }
            else if (pad.DPad.Left == ButtonState.Pressed)
            {
                _spin += 0.05f;
            }
            else
            {
                _spin *= 0.9f;
            }
        }

        private void HandleRotationOnGamePad(GamePadState pad)
        {
            var thumb = pad.ThumbSticks.Left.X;
            if (thumb != 0)
            {
                _spin -= thumb*0.1f;
            }
            else
            {
                _spin *= 0.9f;
            }
        }

        private Vector2 ApplyFriction()
        {
            return (Velocity.Length() > 0)
                       ? -Velocity*FrictionFactor
                       : new Vector2(0, 0);
        }

        private Vector2 ApplyThrust(float factor)
        {
            var thrust = MaxThrust*factor;

            var thrustDirection = new Vector2(
                (float) (-thrust*Math.Sin(Rotation)),
                (float) (thrust*Math.Cos(Rotation))
                );

            var acc = new Vector2(Acceleration.X + thrustDirection.X, Acceleration.Y + thrustDirection.Y);

            return acc.Length() > MaxAccel
                       ? Vector2.Normalize(acc)*MaxAccel
                       : acc;
        }

        private void ClampVelocity()
        {
            if (Velocity.Length() > MaxVelocity)
            {
                Velocity = Vector2.Normalize(Velocity)*MaxVelocity;
            }
        }

        public override void Draw(GameTime gameTime, SpriteBatch batch)
        {
            var src = _spriteMap.GetBoundsForFrame(0, _thrust);

            batch.Draw(_spriteSheet, Position, src, Color.White, Rotation + Angle180, new Vector2(32f, 32f), 1.0f,
                       SpriteEffects.None, 0f);

            //batch.DrawString(_font, _thrust.ToString(), new Vector2(10, 10), Color.DarkBlue);
            //batch.DrawString(_font, Acceleration.Length().ToString(), new Vector2(10, 30), Color.DarkBlue);

            base.Draw(gameTime, batch);
        }

        public bool LaserNotReady()
        {
            var elasped = (DateTime.Now - _lastLaserFire);
            return elasped < TimeSpan.FromSeconds(0.25);
        }

        public void FireLaser()
        {
            if (LaserNotReady()) return;

            _lastLaserFire = DateTime.Now;
            _blastsManager.FireBlaster(this);
        }
    }
}